package tul.cot;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.lang.NotImplementedException;

import tul.cot.job.Migrations;


/**
 * This class enables to count tree statistics of a single segment.
 */
public class MigStats {
	
	/**
	 * A class representing a transition between two labels (from -> to). The class implements
	 * {@link Comparable} interface and overrides equals and hashCode.
	 */
	public static class Change implements Comparable<Change> {
		public final String from, to;
		private final int hashCode;
		
		public Change(String from, String to) {
			this.from = from;
			this.to = to;
			this.hashCode = (from + ";" + to).hashCode();
		}
		
		@Override
		public boolean equals(Object obj) {
			if (!(obj instanceof Change))
				return false;
			Change other = (Change) obj;
			
			return (this.from.equals(other.from) && this.to.equals(other.to));
		}
		
		@Override
		public int hashCode() {
			return hashCode;
		}
		
		@Override
		public String toString() {
			return String.format("%s%s%s", from, Migrations.PAIR_SEP, to);
		}

		@Override
		public int compareTo(Change other) {
			int cmp_from = this.from.compareTo(other.from);
			if (cmp_from != 0)
				return cmp_from;
			
			int cmp_to = this.to.compareTo(other.to);
			if (cmp_to != 0)
				return cmp_to;
			
			return 0;
		}
	}
	
	public final int segNum;
	public final double tMax;
	public final double tMin;
	
	
	public int changes = 0;
	public int no_changes = 0;
	public int splits = 0;
	public int firstSplits = 0;
	public int in = 0;
	public int out = 0;

	/**
	 * In this map the number of changes are counted
	 */
	private Map<Change, Integer> counter = new HashMap<Change, Integer>();
	
	public MigStats(int segNum, double tMax, double tMin) {
		this.segNum = segNum;
		this.tMin = tMin;
		this.tMax = tMax;
	}
	
	public Set<Entry<Change, Integer>> getCounters() {
		return counter.entrySet();
	}
	
//	public int getChangeNum(char from, char to) {
//		return counter.get(new Change(from, to));
//	}
	
	public int getChangeCounter(Change change) {
		Integer ret = counter.get(change);
		return ret != null ? ret : 0;
	}
	
	/**
	 * Increments the counter of Change(from,to) by the specified value.
	 * @param from
	 * @param to
	 * @param value
	 */
	public void incChangeNum(String from, String to, int value) {
		Change change = new Change(from, to);
		Integer cnt = counter.get(change);
		if (cnt == null)
			cnt = 0;
		counter.put(change, cnt + value);
	}
	
	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof MigStats))
			return false;
		MigStats other = (MigStats) obj;
		
		if (this.segNum != other.segNum) return false;
		if (this.tMax != other.tMax) return false;
		if (this.tMin != other.tMin) return false;
		
		if (this.in != other.in) return false;
		if (this.out !=	other.out) return false;
		if (this.splits != other.splits) return false;
		if (this.firstSplits !=	other.firstSplits) return false;
		if (this.changes !=	other.changes) return false;
		if (this.no_changes != other.no_changes) return false;
		
		return this.counter.equals(other.counter);
	}
	
	@Override
	public int hashCode() {
		throw new NotImplementedException();
	}
	
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(String.format("[%f, %f)\n", tMax, tMin));
		sb.append(String.format("SEG: %d\n", segNum));
		sb.append(String.format("IN: %d\tOUT: %d\n", in, out));
		sb.append(String.format("CHANGES: %d\tNO_CHANGES: %d\n", changes, no_changes));
		sb.append(String.format("SPLITS: %d\tFIRST_SPLITS: %d\n", splits, firstSplits));
		sb.append(String.format("SE: %d\tNSE: %d\n", changes + no_changes, in - firstSplits));
		for (Entry<Change, Integer> e: counter.entrySet()) {
			sb.append("" + e + "\n");
		}
		
		return sb.toString();
	}

	public int getTotalDispersals() {
		return changes + no_changes;
	}

	public int getRealisedDispersals() {
		return changes;
	}

	public int getOutLineages() {
		return out;
	}
}